// Copyright 2017 The Closure Rules Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package io.bazel.rules.closure.webfiles;

import static com.google.common.base.Preconditions.checkArgument;

import io.bazel.rules.closure.webfiles.BuildInfo.WebfileInfo;
import io.bazel.rules.closure.webfiles.BuildInfo.WebfileManifestInfo;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;

/** Utility class for reading and writing webfiles to disk. */
public final class WebfilesUtils {

  /** Extension used for protobufs generated by web rules which contain web files and links. */
  public static final String MANIFEST_EXTENSION = ".pb";

  /** Extension used for incremental zip files produced by web rules containing web files. */
  public static final String INCREMENTAL_ZIP_EXTENSION = ".i.zip";

  /** Buffer size we use for disk I/O when buffering is a good idea and we are able to customize. */
  public static final int BUFFER_SIZE = 8192;

  /** Reads webfiles proto manifest from disk. */
  public static WebfileManifestInfo readManifest(Path path) throws IOException {
    checkManifestExtension(path);
    try (InputStream input = new BufferedInputStream(Files.newInputStream(path), BUFFER_SIZE)) {
      return WebfileManifestInfo.parseFrom(input);
    }
  }

  /** Writes webfiles proto manifest to disk. */
  public static void writeManifest(Path path, WebfileManifestInfo info) throws IOException {
    checkManifestExtension(path);
    try (OutputStream output = new BufferedOutputStream(Files.newOutputStream(path), BUFFER_SIZE)) {
      info.writeTo(output);
    }
  }

  /**
   * Returns path of zip accompanying a particular {@link WebfileManifestInfo} file.
   *
   * <p>The incremental zip file is assumed to exist in the same directory and on the same file
   * system as the proto manifest.
   *
   * <p>Incremental zip files contain only files that pertain to a single stage of compilation; it
   * does not include dependencies. Each and every web rule declared in a BUILD file creates its own
   * incremental zip file. Normally this file will just contain the srcs it listed. But it might
   * also contain intermediate files that help the compiler do its job, e.g. AST objects or embedded
   * content pulled out of HTML documents. It isn't until {@code WebDeployer} is run that these
   * incremental files can be coalesced into a deploy archive that is helpful to the user.
   */
  public static Path getIncrementalZipPath(Path manifest) {
    checkManifestExtension(manifest);
    String base = manifest.getFileName().toString();
    String name =
        base.substring(0, base.length() - MANIFEST_EXTENSION.length()) + INCREMENTAL_ZIP_EXTENSION;
    Path parent = manifest.getParent();
    return parent == null ? manifest.getFileSystem().getPath(name) : parent.resolve(name);
  }

  /**
   * Returns the name {@code webfile} would have when stored inside a .zip or .i.zip archive.
   *
   * <p>Please note that within a particular zip archive, names must be unique. Intermediate files,
   * such as AST representations, must do something like append a file extension.
   */
  public static String getZipEntryName(WebfileInfo webfile) {
    checkArgument(webfile.getWebpath().startsWith("/") && !webfile.getWebpath().endsWith("/"));
    return webfile.getWebpath().substring(1);
  }

  private static void checkManifestExtension(Path path) {
    checkArgument(
        path.toString().endsWith(MANIFEST_EXTENSION),
        "manifest must have %s extension: %s",
        MANIFEST_EXTENSION,
        path);
  }

  private WebfilesUtils() {}
}
